home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / UNITRK.ZIP / munitrk.c next >
C/C++ Source or Header  |  1995-01-24  |  7KB  |  340 lines

  1. /*
  2.  
  3.     UNITRK.C
  4.  
  5.     All routines dealing with the manipulation of UNITRK(tm) streams
  6.  
  7. */
  8.  
  9. #include <malloc.h>
  10. #include <string.h>
  11. #include "munitrk.h"
  12.  
  13.  
  14. #define BUFPAGE  128            // smallest unibuffer size
  15. #define TRESHOLD 16
  16.  
  17. /* unibuffer is increased by BUFPAGE
  18.   bytes when unipc reaches unimax-TRESHOLD */
  19.  
  20.  
  21.  
  22. /*
  23.     Ok.. I'll try to explain the new internal module format.. so here it goes:
  24.  
  25.  
  26.     The UNITRK(tm) Format:
  27.     ======================
  28.  
  29.     A UNITRK stream is an array of bytes representing a single track
  30.     of a pattern. It's made up of 'repeat/length' bytes, opcodes and
  31.     operands (sort of a assembly language):
  32.  
  33.     rrrlllll
  34.     [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..
  35.     ^                                         ^ ^
  36.     |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...
  37.  
  38.  
  39.     The rep/len byte contains the number of bytes in the current row,
  40.     _including_ the length byte itself (So the LENGTH byte of row 0 in the
  41.     previous example would have a value of 5). This makes it easy to search
  42.     through a stream for a particular row. A track is concluded by a 0-value
  43.     length byte.
  44.  
  45.     The upper 3 bits of the rep/len byte contain the number of times -1 this
  46.     row is repeated for this track. (so a value of 7 means this row is repeated
  47.     8 times)
  48.  
  49.     Opcodes can range from 1 to 255 but currently only opcodes 1 to 19 are
  50.     being used. Each opcode can have a different number of operands. You can
  51.     find the number of operands to a particular opcode by using the opcode
  52.     as an index into the 'unioperands' table.
  53.  
  54. */
  55.  
  56.  
  57.  
  58. UWORD unioperands[256]={
  59.     0,              // not used
  60.     1,              // UNI_NOTE
  61.     1,              // UNI_INSTRUMENT
  62.     1,              // UNI_PTEFFECT0
  63.     1,              // UNI_PTEFFECT1
  64.     1,              // UNI_PTEFFECT2
  65.     1,              // UNI_PTEFFECT3
  66.     1,              // UNI_PTEFFECT4
  67.     1,              // UNI_PTEFFECT5
  68.     1,              // UNI_PTEFFECT6
  69.     1,              // UNI_PTEFFECT7
  70.     1,              // UNI_PTEFFECT8
  71.     1,              // UNI_PTEFFECT9
  72.     1,              // UNI_PTEFFECTA
  73.     1,              // UNI_PTEFFECTB
  74.     1,              // UNI_PTEFFECTC
  75.     1,              // UNI_PTEFFECTD
  76.     1,              // UNI_PTEFFECTE
  77.     1,              // UNI_PTEFFECTF
  78.     1,                // UNI_S3MEFFECTA
  79.     1,              // UNI_S3MEFFECTD
  80.     1,              // UNI_S3MEFFECTE
  81.     1,              // UNI_S3MEFFECTF
  82.     1,              // UNI_S3MEFFECTI
  83.     1,              // UNI_S3MEFFECTQ
  84.     1,                // UNI_S3MEFFECTT
  85. };
  86.  
  87.  
  88. UBYTE *unibuf;        // pointer to the temporary unitrk buffer
  89. UWORD unimax;        // maximum number of bytes to be written to this buffer
  90.  
  91. UWORD unipc;        // index in the buffer where next opcode will be written
  92. UWORD unitt;        // holds index of the rep/len byte of a row
  93. UWORD lastp;        // holds index to the previous row (needed for compressing)
  94.  
  95. UBYTE *rowstart;    // startadress of a row
  96. UBYTE *rowend;      // endaddress of a row (exclusive)
  97. UBYTE *rowpc;       // current unimod(tm) programcounter
  98.  
  99.  
  100. void UniSetRow(UBYTE *t)
  101. {
  102.     rowstart=t;
  103.     rowpc=rowstart;
  104.     rowend=rowstart+(*(rowpc++)&0x1f);
  105. }
  106.  
  107.  
  108. UBYTE UniGetByte(void)
  109. {
  110.     return (rowpc<rowend) ? *(rowpc++) : 0;
  111. }
  112.  
  113.  
  114. void UniSkipOpcode(UBYTE op)
  115. {
  116.     UWORD t=unioperands[op];
  117.     while(t--) UniGetByte();
  118. }
  119.  
  120.  
  121.  
  122. UBYTE *UniFindRow(UBYTE *t,UWORD row)
  123. /*
  124.     Finds the address of row number 'row' in the UniMod(tm) stream 't'
  125.  
  126.     returns NULL if the row can't be found.
  127. */
  128. {
  129.     UBYTE c,l;
  130.  
  131.     while(1){
  132.  
  133.         c=*t;                    // get rep/len byte
  134.  
  135.         if(!c) return NULL;        // zero ? -> end of track..
  136.  
  137.         l=(c>>5)+1;                // extract repeat value
  138.  
  139.         if(l>row) break;        // reached wanted row? -> return pointer
  140.  
  141.         row-=l;                    // havn't reached row yet.. update row
  142.         t+=c&0x1f;                // point t to the next row
  143.     }
  144.  
  145.     return t;
  146. }
  147.  
  148.  
  149.  
  150. void UniReset(void)
  151. /*
  152.     Resets index-pointers to create a new track.
  153. */
  154. {
  155.     unitt=0;        // reset index to rep/len byte
  156.     unipc=1;        // first opcode will be written to index 1
  157.     lastp=0;        // no previous row yet
  158.     unibuf[0]=0;    // clear rep/len byte
  159. }
  160.  
  161.  
  162.  
  163. void UniWrite(UBYTE data)
  164. /*
  165.     Appends one byte of data to the current row of a track.
  166. */
  167. {
  168.     // write byte to current position and update
  169.  
  170.     unibuf[unipc++]=data;
  171.  
  172.     // Check if we've reached the end of the buffer
  173.  
  174.     if(unipc>(unimax-TRESHOLD)){
  175.  
  176.         UBYTE *newbuf;
  177.  
  178.         /* We've reached the end of the buffer, so expand
  179.            the buffer by BUFPAGE bytes */
  180.  
  181.         newbuf=realloc(unibuf,unimax+BUFPAGE);
  182.  
  183.         // Check if realloc succeeded
  184.  
  185.         if(newbuf!=NULL){
  186.             unibuf=newbuf;
  187.             unimax+=BUFPAGE;
  188.         }
  189.         else{
  190.             /* realloc failed, so decrease unipc so we won't write beyond
  191.                the end of the buffer.. I don't report the out-of-memory
  192.                here; the UniDup() will fail anyway so that's where the
  193.                loader sees that something went wrong */
  194.  
  195.             unipc--;
  196.         }
  197.     }
  198. }
  199.  
  200.  
  201.  
  202. void UniInstrument(UBYTE ins)
  203. /*
  204.     Appends UNI_INSTRUMENT opcode to the unitrk stream.
  205. */
  206. {
  207.     UniWrite(UNI_INSTRUMENT);
  208.     UniWrite(ins);
  209. }
  210.  
  211.  
  212.  
  213. void UniNote(UBYTE note)
  214. /*
  215.     Appends UNI_NOTE opcode to the unitrk stream.
  216. */
  217. {
  218.     UniWrite(UNI_NOTE);
  219.     UniWrite(note);
  220. }
  221.  
  222.  
  223. void UniPTEffect(UBYTE eff,UBYTE dat)
  224. /*
  225.     Appends UNI_PTEFFECTX opcode to the unitrk stream.
  226. */
  227. {
  228.     if(eff!=0 || dat!=0){                // don't write empty effect
  229.         UniWrite(UNI_PTEFFECT0+eff);
  230.         UniWrite(dat);
  231.     }
  232. }
  233.  
  234.  
  235.  
  236. BOOL MyCmp(UBYTE *a,UBYTE *b,UWORD l)
  237. {
  238.     UWORD t;
  239.  
  240.     for(t=0;t<l;t++){
  241.         if(*(a++)!=*(b++)) return 0;
  242.     }
  243.     return 1;
  244. }
  245.  
  246.  
  247.  
  248. void UniNewline(void)
  249. /*
  250.     Closes the current row of a unitrk stream (updates the rep/len byte)
  251.     and sets pointers to start a new row.
  252. */
  253. {
  254.     UWORD n,l,len;
  255.  
  256.     n=(unibuf[lastp]>>5)+1;        // repeat of previous row
  257.     l=(unibuf[lastp]&0x1f);        // length of previous row
  258.  
  259.     len=unipc-unitt;            // length of current row
  260.  
  261.     /* Now, check if the previous and the current row are identical..
  262.        when they are, just increase the repeat field of the previous row */
  263.  
  264.     if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)){
  265.         unibuf[lastp]+=0x20;
  266.         unipc=unitt+1;
  267.     }
  268.     else{
  269.         // current and previous row aren't equal.. so just update the pointers
  270.  
  271.         unibuf[unitt]=len;
  272.         lastp=unitt;
  273.         unitt=unipc;
  274.         unipc++;
  275.     }
  276. }
  277.  
  278.  
  279.  
  280.  
  281. UBYTE *UniDup(void)
  282. /*
  283.     Terminates the current unitrk stream and returns a pointer
  284.     to a copy of the stream.
  285. */
  286. {
  287.     UBYTE *d;
  288.  
  289.     unibuf[unitt]=0;
  290.  
  291.     if((d=malloc(unipc))==NULL){
  292.         myerr=ERROR_ALLOC_STRUCT;
  293.         return NULL;
  294.     }
  295.     memcpy(d,unibuf,unipc);
  296.  
  297.     return d;
  298. }
  299.  
  300.  
  301.  
  302. UWORD TrkLen(UBYTE *t)
  303. /*
  304.     Determines the length (in rows) of a unitrk stream 't'
  305. */
  306. {
  307.     UWORD len=0;
  308.     UBYTE c;
  309.  
  310.     while(c=*t&0x1f){
  311.         len+=c;
  312.         t+=c;
  313.     }
  314.     len++;
  315.  
  316.     return len;
  317. }
  318.  
  319.  
  320.  
  321. BOOL UniInit(void)
  322. {
  323.     unimax=BUFPAGE;
  324.  
  325.     if(!(unibuf=malloc(unimax))){
  326.         myerr=ERROR_ALLOC_STRUCT;
  327.         return 0;
  328.     }
  329.     return 1;
  330. }
  331.  
  332.  
  333.  
  334. void UniCleanup(void)
  335. {
  336.     if(unibuf!=NULL) free(unibuf);
  337.     unibuf=NULL;
  338. }
  339.  
  340.